﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Tomahawk.Resources.Physics;
using Tomahawk.Runtime.Rendering.Materials;

namespace Tomahawk.Runtime.Utils
{
    public sealed class MeshUtils
    {
        public static void DrawMesh(ModelMesh mesh, GraphicsDevice device)
        {
            device.Indices = mesh.IndexBuffer;

            foreach (ModelMeshPart part in mesh.MeshParts)
            {
                Effect effect = part.Effect;

                effect.Begin();
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    device.VertexDeclaration = part.VertexDeclaration;
                    device.Vertices[0].SetSource(
                        mesh.VertexBuffer,
                        part.StreamOffset,
                        part.VertexStride);
                    device.DrawIndexedPrimitives(
                        PrimitiveType.TriangleList,
                        part.BaseVertex,
                        0,
                        part.NumVertices,
                        part.StartIndex,
                        part.PrimitiveCount);
                    pass.End();
                }
                effect.End();
            }
        }

        public static void ConvertToCollisionMesh(Model model, string descName)
        {
            PhysicsActorDesc actorDesc = new PhysicsActorDesc();
            PhysicsBodyDesc bodyDesc = new PhysicsBodyDesc();
            PhysicsMeshShapeDesc meshDesc = new PhysicsMeshShapeDesc();

            meshDesc.LoadFromModel(model);
            bodyDesc.shapes.Add(meshDesc);

            bodyDesc.name = "root";
            actorDesc.bodies.Add(bodyDesc);
            actorDesc.type = PhysicsActorType.StaticActor;

            using (FileStream fs = new FileStream(descName, FileMode.Create, FileAccess.Write))
            {
                XmlSerializer x = new XmlSerializer(typeof(PhysicsActorDesc));
                x.Serialize(fs, actorDesc);
            }
        }

        public static void ExtractData(List<Vector3> vertices, List<Vector3> indices, Model model)
        {
            Matrix[] bones_ = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(bones_);
            foreach (ModelMesh mm in model.Meshes)
            {
                Matrix xform = bones_[mm.ParentBone.Index];
                foreach (ModelMeshPart mmp in mm.MeshParts)
                {
                    int offset = vertices.Count;
                    Vector3[] a = new Vector3[mmp.NumVertices];
                    mm.VertexBuffer.GetData<Vector3>(mmp.StreamOffset + mmp.BaseVertex * mmp.VertexStride,
                        a, 0, mmp.NumVertices, mmp.VertexStride);
                    for (int i = 0; i != a.Length; ++i)
                        Vector3.Transform(ref a[i], ref xform, out a[i]);
                    vertices.AddRange(a);

                    if (mm.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits)
                        throw new Exception(
                            String.Format("Model uses 32-bit indices, which are not supported."));
                    short[] s = new short[mmp.PrimitiveCount * 3];
                    mm.IndexBuffer.GetData<short>(mmp.StartIndex * 2, s, 0, mmp.PrimitiveCount * 3);

                    Vector3[] tvi = new Vector3[mmp.PrimitiveCount];

                    for (int i = 0; i != mmp.PrimitiveCount; ++i)
                    {
                        tvi[i].X = s[i * 3 + 2] + offset;
                        tvi[i].Y = s[i * 3 + 1] + offset;
                        tvi[i].Z = s[i * 3 + 0] + offset;
                    }
                    indices.AddRange(tvi);
                }
            }
        }
    }
}
